 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.registry;

 import java.util.Arrays ;
 import java.util.Collections ;
 import java.util.Comparator ;

 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtension;
 import org.eclipse.core.runtime.IExtensionPoint;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.ui.internal.WorkbenchPlugin;

 /**
  * Template implementation of a registry reader that creates objects
  * representing registry contents. Typically, an extension
  * contains one element, but this reader handles multiple
  * elements per extension.
  *
  * To start reading the extensions from the registry for an
  * extension point, call the method <code>readRegistry</code>.
  *
  * To read children of an IConfigurationElement, call the
  * method <code>readElementChildren</code> from your implementation
  * of the method <code>readElement</code>, as it will not be
  * done by default.
  */
 public abstract class RegistryReader {
     
     // for dynamic UI - remove this cache to avoid inconsistency
 //protected static Hashtable extensionPoints = new Hashtable();
 /**
      * The constructor.
      */
     protected RegistryReader() {
     }

     /**
      * Logs the error in the workbench log using the provided
      * text and the information in the configuration element.
      */
     protected static void logError(IConfigurationElement element, String text) {
         IExtension extension = element.getDeclaringExtension();
         StringBuffer buf = new StringBuffer ();
         buf
                 .append("Plugin " + extension.getNamespace() + ", extension " + extension.getExtensionPointUniqueIdentifier());//$NON-NLS-2$//$NON-NLS-1$
 buf.append("\n" + text);//$NON-NLS-1$
 WorkbenchPlugin.log(buf.toString());
     }

     /**
      * Logs a very common registry error when a required attribute is missing.
      */
     protected static void logMissingAttribute(IConfigurationElement element,
             String attributeName) {
         logError(element,
                 "Required attribute '" + attributeName + "' not defined");//$NON-NLS-2$//$NON-NLS-1$
 }

     /**
      * Logs a very common registry error when a required child is missing.
      */
     protected static void logMissingElement(IConfigurationElement element,
             String elementName) {
         logError(element,
                 "Required sub element '" + elementName + "' not defined");//$NON-NLS-2$//$NON-NLS-1$
 }

     /**
      * Logs a registry error when the configuration element is unknown.
      */
     protected static void logUnknownElement(IConfigurationElement element) {
         logError(element, "Unknown extension tag found: " + element.getName());//$NON-NLS-1$
 }

     /**
      * Apply a reproducable order to the list of extensions
      * provided, such that the order will not change as
      * extensions are added or removed.
      * @param extensions the extensions to order
      * @return ordered extensions
      */
     public static IExtension[] orderExtensions(IExtension[] extensions) {
         // By default, the order is based on plugin id sorted
 // in ascending order. The order for a plugin providing
 // more than one extension for an extension point is
 // dependent in the order listed in the XML file.
 IExtension[] sortedExtension = new IExtension[extensions.length];
         System.arraycopy(extensions, 0, sortedExtension, 0, extensions.length);
         Comparator comparer = new Comparator () {
             public int compare(Object arg0, Object arg1) {
                 String s1 = ((IExtension) arg0).getNamespace();
                 String s2 = ((IExtension) arg1).getNamespace();
                 return s1.compareToIgnoreCase(s2);
             }
         };
         Collections.sort(Arrays.asList(sortedExtension), comparer);
         return sortedExtension;
     }

     /**
      * Implement this method to read element's attributes.
      * If children should also be read, then implementor
      * is responsible for calling <code>readElementChildren</code>.
      * Implementor is also responsible for logging missing
      * attributes.
      *
      * @return true if element was recognized, false if not.
      */
     protected abstract boolean readElement(IConfigurationElement element);

     /**
      * Read the element's children. This is called by
      * the subclass' readElement method when it wants
      * to read the children of the element.
      */
     protected void readElementChildren(IConfigurationElement element) {
         readElements(element.getChildren());
     }

     /**
      * Read each element one at a time by calling the
      * subclass implementation of <code>readElement</code>.
      *
      * Logs an error if the element was not recognized.
      */
     protected void readElements(IConfigurationElement[] elements) {
         for (int i = 0; i < elements.length; i++) {
             if (!readElement(elements[i])) {
                 logUnknownElement(elements[i]);
             }
         }
     }

     /**
      * Read one extension by looping through its
      * configuration elements.
      */
     protected void readExtension(IExtension extension) {
         readElements(extension.getConfigurationElements());
     }

     /**
      * Start the registry reading process using the
      * supplied plugin ID and extension point.
      *
      * @param registry the registry to read from
      * @param pluginId the plugin id of the extenion point
      * @param extensionPoint the extension point id
      */
     public void readRegistry(IExtensionRegistry registry, String pluginId,
             String extensionPoint) {
         IExtensionPoint point = registry.getExtensionPoint(pluginId,
                 extensionPoint);
         if (point == null) {
             return;
         }
         IExtension[] extensions = point.getExtensions();
         extensions = orderExtensions(extensions);
         for (int i = 0; i < extensions.length; i++) {
             readExtension(extensions[i]);
         }
     }
     
     /**
      * Utility for extracting the description child of an element.
      *
      * @param configElement the element
      * @return the description
      * @since 3.1
      */
     public static String getDescription(IConfigurationElement configElement) {
         IConfigurationElement[] children = configElement.getChildren(IWorkbenchRegistryConstants.TAG_DESCRIPTION);
         if (children.length >= 1) {
             return children[0].getValue();
         }
         return "";//$NON-NLS-1$
 }
     
     /**
      * Utility for extracting the value of a class attribute or a nested class
      * element that follows the pattern set forth by
      * {@link org.eclipse.core.runtime.IExecutableExtension}.
      *
      * @param configElement
      * the element
      * @param classAttributeName
      * the name of the class attribute to check
      * @return the value of the attribute or nested class element
      * @since 3.1
      */
     public static String getClassValue(IConfigurationElement configElement, String classAttributeName) {
         String className = configElement.getAttribute(classAttributeName);
         if (className != null) {
             return className;
         }
         IConfigurationElement [] candidateChildren = configElement.getChildren(classAttributeName);
         if (candidateChildren.length == 0) {
             return null;
         }
     
         return candidateChildren[0].getAttribute(IWorkbenchRegistryConstants.ATT_CLASS);
     }
 }

